#include "FKObject.h"

#include "FKLogger.h"
#include "FKEventNames.h"

//#include <QLinkedList>
#include <QMapIterator>
#include <QStack>
#include <QStringList>

#include "FKEvent.h"
#include "FKRoom.h"
#include "FKAbstractObjectManager.h"


QHash<qint32,FKObject*> FKObject::_refs;
QStack<qint32> FKObject::_freed;
FKAbstractObjectManager* FKObject::_manager=0;
qint32 FKObject::_nextId=0;

/*!
 * \class FKObject
 * \inmodule FKObjects
 * \brief Это базовый класс, реализующий концепцию управления объектами
 *
 * FKFramework предлагает собственную концепцию управления объектами (КУО)
 * КУО включает следующие основопологающие принципы:
 * \list
 *  \li Реестр объектов. Каждый объект имеет уникальный номер и может быть обнаружен по тому же номеру как на стороне сервера, так и на стороне любого клиента, если он там существует. Нумерация начинается с 0. Неопределенный номер равен -1
 *  \li Единство комнаты. Комната создает объекты и управляет ими. Объекты комнаты удаляются вместе с комнатой.
 *  \li Единство объекта. Объект создается и удаляется только на стороне сервера.
 *  \li Видимость. Объекты могут следить за другими объектами. На стороне видящего клиента объект гарантировано существует. Объект, пропавший из видимости клиента, удаляется. Действия и события могут приходить только для видимых объектов.
 *  \li Иерархия видимости. Видимость не должна иметь цикличности имеет 4 типа:
 *
 *      \list
 *
 *          \li минимальную - только общие свойства
 *          \li специальную - общие и уникальные свойства
 *          \li общую       - общие свойства и видимость всех далее видимых объектов
 *          \li полную      - все свойства и видимость всех далее видимых объектов
 *      \endlist
 *  \li Уникальные свойства. Объекты могут иметь свойства, видимые только определенному кругу клиентов. На стороне других клиентов значение свойства не обновляется.
 *  \li Общие свойства. Объекты могут иметь свойства, немедленно синхронизируемые на всех клиентах.
 *  \li События. Сервер может генерировать и отправлять для выполнения на стороне клиента события. Событие считается безусловно валидным и сразу передается к исполнению.
 *  \li Действия. Клиенты направляют на сервер запросы на выполнение действий. Система автоматически проверяет допустимость такого запроса и, в случае успеха, передает действие к исполению.
 *\endlist
 * FKObject реализует все необходимые инструменты для использования КУО.
 *
 * При наследовании класса необходимо использовать макросы FK_OBJECT() и FK_EVENTS(){}
 */

/*!
 * \macro FK_OBJECT(...)
 *
 * \brief This macro must be placed in class definition instead of class specifier.
 *
 * Macro takes 2 arguments: className and inheritedClassName. The first '{' must be omitted.
\code
class FKOBJECTSSHARED_EXPORT FK_OBJECT(FKClient,FKObject)
    Q_OBJECT
public:
    FKCilent(FKRoom* room=0;qint32 id=-1);
    ~FKClient();
};
\endcode
 */

/*!
 * \macro FK_EVENTS(...)
 *
 * \brief This macro must be placed in *.cpp file for every class.
 *
 * Macro takes 1 argument: className.
 *
 * Instruction block {} after macro is required. Block determine class-specific events, actions and properties or can be empty.
\code
FK_EVENTS(FKCilent){
    events.insert(FKEventName::G_createObjects,&FKClient::E_createObjects);
    events.insert(FKEventName::G_deleteObjects,&FKClient::E_deleteObjects);
    actions.insert(FKEventName::A_setPassword,&FKClient::A_setPassword);
    props.append(FKEventName::C_password);
}
\endcode
 */

/*!
 * \brief Создает объект в комнате room.
 *
 * Для любого создаваемого в комнате объекта необходимо указывать эту комнату. Второй аргумент используется только системой для генерации объектов на стороне клиента.
 *
 * Server-side constructing objects must have default id to get servant.
 *
 * Client-side objects must be autocreated with valid id.
 */
FKObject::FKObject(FKRoom* room, qint32 objectID):FKSystemObject(),_initialized(false){
    FK_CBEGIN
    _room=0;
    if(objectID<0){
        servant=new FKObject::Servant();
        objectID=reserveId();
    }else{
        servant=0;
    }
    submitId(objectID);
    setRoom(room);
    connect(this,SIGNAL(newEvent(FKEvent*)),_manager,SLOT(internalEvent(FKEvent*)));
    FK_CEND
}

/*!
 * \brief Should not be used. Use deleteFKObject() instead
 */
FKObject::~FKObject(){
    FK_DBEGIN
    if(servant)delete servant;
    FK_DEND
}

qint32 FKObject::reserveId(){
    if(_freed.isEmpty()){
        return _nextId++;
    }else{
        return _freed.pop();
    }
}

void FKObject::submitId(const qint32 &id){
    if(_refs.contains(id)){
        FK_MLOGV("Object id already registered",id)
    }
    _objID=id;
    _refs.insert(id,this);
}

void FKObject::releaseId(){
    if(_objID<0){
        FK_MLOG("Release negative id")
    }else{
        _refs.remove(_objID);
        _freed.push(_objID);
//        FK_MLOGV("Release object id",_objID)
    }
    _objID=-1;
}

/*!
 * \brief Use this to safe deletion instead of deleteLater() or delete operator
 */
void FKObject::deleteFKObject(){
    if(servant){
        for(QMap<QString, QLinkedList<FKObject*> >::ConstIterator i=servant->actors.constBegin();i!=servant->actors.constEnd();++i){
            foreach(FKObject* obj, i.value())forbidControlBy(obj,i.key());
        }
        for(QMap<QString, QLinkedList<FKObject*> >::ConstIterator i=servant->controlled.constBegin();i!=servant->controlled.constEnd();++i){
            foreach(FKObject* obj, i.value())obj->forbidControlBy(this,i.key());
        }
        foreach(FKObject* obj, servant->sharedWatchers) {
            obj->stopSharedWatching(this);
        }
        foreach(FKObject* obj, servant->sharedWatcheds) {
            stopSharedWatching(obj);
        }
        for(QMap<QString, QSet<FKObject*> >::ConstIterator i=servant->watchers.constBegin();i!=servant->watchers.constEnd();++i){
            foreach(FKObject* obj, i.value())obj->stopCustomWatching(this,i.key());
        }
        for(QMap<QString, QSet<FKObject*> >::ConstIterator i=servant->watcheds.constBegin();i!=servant->watcheds.constEnd();++i){
            foreach(FKObject* obj, i.value())stopCustomWatching(obj,i.key());
        }
    }
    emit deletedFKObject(this);
    setRoom(0);
    releaseId();
    deleteLater();
}

/*!
 * \brief Pack object to stream. Overriden FKSystemObject function
 */
void FKObject::packObject(QDataStream &stream){
    QString className(metaObject()->className());
    qint32 id=getID();
    stream<<className;
    stream<<id;
    pack(stream);
    stream<<className;
}
/*!
 * \brief Delete all registered FKObjects. This function is safe and can be used for reset app state
 */
void FKObject::deleteAll(){
    foreach(FKObject* obj,_refs){
        obj->deleteFKObject();
    }
}

/*!
 * \brief Move object to room
 */
void FKObject::setRoom(FKRoom* room){
    if(room==_room)return;
    FKRoom* oldRoom=_room;
    _room=room;
    if(oldRoom)oldRoom->removeObject(this);
    if(room)room->addObject(this);
    if(servant){
        if(oldRoom)disconnect(oldRoom,SIGNAL(deletedFKObject(FKObject*)),this,SLOT(deleteFKObject()));
        if(room)connect(room,SIGNAL(deletedFKObject(FKObject*)),this,SLOT(deleteFKObject()));
    }
}

/*!
 * \brief Set object manager. This must be done before creating any objects
 */
void FKObject::setManager(FKAbstractObjectManager *manager){
    _manager=manager;
}

/*!
 * \brief Returns set of clients, that have shared vision of object
 */

QSet<FKObject*> FKObject::sharedVisors(){
    if(!servant){
        FK_MLOG("error getting shared visors: no servant")
        return QSet<FKObject*>();
    }
    if(servant->durty){
        QSet<FKObject*> sv;
        foreach(FKObject* obj,servant->sharedWatchers)sv.unite(obj->sharedVisors());
        servant->visors=sv;
        servant->durty=false;
    }
    return servant->visors;
}

/*!
 * \brief Returns set of clients, that have any vision of object
 */

QSet<FKObject*> FKObject::totalVisors() const{
    return customVisors(FKEventName::g_commonProperty);
}

/*!
 * \brief Returns set of all watched objects (primary or shared)
 */

QSet<FKObject*> FKObject::totalWatched()const{
    if(!servant){
        FK_MLOG("error getting watched set: no servant")
        return QSet<FKObject*>();
    }
    QSet<FKObject*> w(servant->watcheds.value(FKEventName::g_commonProperty));
    foreach (FKObject* obj, servant->sharedWatcheds) {
        w.unite(obj->totalWatched());
    }
    return w;
}

/*!
 * \brief Returns set of clients, that have vision of property
 */

QSet<FKObject*> FKObject::customVisors(const QString &prop) const{
    if(!servant){
        FK_MLOG("error getting custom visors: no servant")
        return QSet<FKObject*>();
    }
    QSet<FKObject*> v;
    foreach(FKObject* watcher, servant->watchers.value(prop)){
        v.unite(watcher->sharedVisors());
    }
    return v;
}

void FKObject::resetSharedVisorsCalculation(){
    servant->durty=true;
    foreach (FKObject* obj, servant->sharedWatcheds) {
        obj->resetSharedVisorsCalculation();
    }
}

/*!
 * \brief Allow object recieve actions of type \c prop from \c actor
 */

bool FKObject::allowControlBy(FKObject* actor,const QString &prop){
    if(!servant){
        FK_MLOG("unable allow controlling: no servant")
        return false;
    }
    if(prop==QString()){
        int count=0;
        foreach(QString p, actionList()){
            if(!servant->actors.value(p).contains(actor)){
                servant->actors[p].append(actor);
                actor->servant->controlled[p].append(this);
                ++count;
            }
        }
        return count;
    }else{
        QStringList list(actionList());
        if(list.contains(prop)){
            if(!servant->actors.value(prop).contains(actor)){
                servant->actors[prop].append(actor);
                actor->servant->controlled[prop].append(this);
                return true;
            }
        }else{
            FK_MLOGV("Allowing unexisting action",prop)
        }
        return false;
    }
}

/*!
 * \brief Forbid object recieve actions of type \c prop from \c actor
 */

bool FKObject::forbidControlBy(FKObject* actor,const QString &prop){
    if(!servant){
        FK_MLOG("unable forbid controlling: no servant")
        return false;
    }
    if(prop==QString()){
        int count=0;
        foreach(QString p, actionList()){
            if(servant->actors.value(p).contains(actor)){
                servant->actors[p].removeOne(actor);
                actor->servant->controlled[p].removeOne(this);
                ++count;
            }
        }
        return count;
    }else{
        QStringList list(actionList());
        if(list.contains(prop)){
            if(servant->actors.value(prop).contains(actor)){
                servant->actors[prop].removeOne(actor);
                actor->servant->controlled[prop].removeOne(this);
                return true;
            }
        }
        return false;
    }
}

/*!
 * \brief Allow \c user request object initiate some actions
 */

bool FKObject::allowEmitBy(FKObject* user){
    if(!servant){
        FK_MLOG("unable allow emitting: no servant")
        return false;
    }
    if(servant->emitters.contains(user->getID())){
        FK_MLOGV("Already allowed emitting",getID())
        return false;
    }else{
        connect(user,SIGNAL(deletedFKObject(FKObject*)),SLOT(forbidEmitBy(FKObject*)));
        servant->emitters.append(user->getID());
        return true;
    }
}

/*!
 * \brief Forbid \c user request object initiate some actions
 */

bool FKObject::forbidEmitBy(FKObject* user){
    if(!servant){
        FK_MLOG("unable forbid emitting: no servant")
        return false;
    }
    if(servant->emitters.contains(user->getID())){
        servant->emitters.removeOne(user->getID());
        disconnect(user,SIGNAL(deletedFKObject(FKObject*)),this,SLOT(forbidEmitBy(FKObject*)));
        return true;
    }else{
        FK_MLOGV("Already forbidden emitting",getID())
        return false;
    }
}

/*!
 * \brief Check action validity and execute on success
 */

void FKObject::processFKAction(FKEvent* action){
    if(servant){
        if(actionList().contains(action->subject())){   //если распознано действие
            FKObject* obj=getObject(action->emitter()); //распознать эмитента
            if(!obj){
                FK_MLOGV("Reject execute action with hidden emitter",getID())
            }else if((obj->getID()==getID() || servant->actors.value(action->subject()).contains(obj)) && obj->servant->emitters.contains(action->owner()) && obj->servant->active){
                //если эмитент - сам объект, или эмитенту позволено совершать действие над объектом
                //и если эмитент управляем пользователем и эмитенту разрешено активировать действия
                executeAction(action);
            }else{
                FK_MLOGV("Try execute forbidden action",getID())
            }
        }else{
            FK_MLOGV("Object has no requested action",action->subject())
        }
    }else{
        FK_MLOG("No servant to process action")
    }
    action->deleteLater();
}

/*!
 * \brief Check event validity and execute on success
 */

void FKObject::processFKEvent(FKEvent* event){
    if(event->isPropertyNotifier){
        if(propList().contains(event->subject())){
            setProperty(event->subject().toLatin1(),event->value());
        }else{
            FK_MLOGV("Object has no requested property",getClassName()+QString(" ")+event->subject())
        }
    }else{
        if(eventList().contains(event->subject())){
            executeEvent(event);
        }else{
            FK_MLOGV("Object has no requested event",getClassName()+QString(" ")+event->subject())
        }
    }
    event->deleteLater();
}

/*!
 * \fn void FKObject::newEvent(FKEvent* event)
 * \brief Emit new event from server
 */

/*!
 * \fn void FKObject::deletedFKObject(FKObject* obj)
 * \brief Emitted from deleteFKObject() function to notify object system
 */

/*!
\enum FKObject::EventTarget

\brief Specify event recievers

\value ToClientsAndServer
        Event sended to distant clients and immidiatly execute on server.
        Use this for executing normal events
\value ToDistantClients
        Event sended to distant clients only.
        Use this for syncronizing clients with server
\value ToAllClients
        Event sended to both distant and server-side clients.
        Use this for notify user interface
 */

/*!
 * \brief Returns object with id. If object not registered or not existed returns 0
 */
FKObject* FKObject::getObject(const qint32 &obj){
    return _refs.value(obj,0);
}

/*!
 * \brief Convinient function to return class name
 */
QString FKObject::getClassName()const{
    return metaObject()->className();
}

/*!
 * \brief Mark property need syncronization
 */
void FKObject::updateProp(const QString &prop){
    if(!servant){
        FK_MLOG("Unable update property: no servant")
        return;
    }
    if(propList().contains(prop)){
        servant->needUpdate[prop]=true;
    }else{
        FK_MLOGV("Object has no such event for update",getID())
    }
}

/*!
 * \brief Syncronize marked properties
 */

void FKObject::applyUpdates(){
    if(!servant){
        FK_MLOG("Unable apply updates: no servant")
        return;
    }
    FKEvent* event;
    QList<qint32> recievers;
    QMapIterator<QString,bool> i(servant->needUpdate);
    while(i.hasNext()){
        if(i.next().value()){
            recievers.clear();
            foreach(FKObject* obj,customVisors(i.key())){
                recievers.append(obj->getID());
            }
            recievers.append(-2);
            event=FKEvent::makeEvent(getID(),i.key(),property(i.key().toUtf8()),recievers);
            event->isPropertyNotifier=true;
            emit newEvent(event);
        }
    }
}

/*!
 * \brief Syncronize all visible for client properties
 */

void FKObject::refreshValues(FKObject* client){
    if(!servant){
        FK_MLOG("Unable refresh values: no servant")
        return;
    }
    FKEvent* event;
    QList<qint32> recievers;
    recievers<<client->getID()<<-2;
    foreach (QString prop, propList()) {
//        FK_MLOGV("property update request",prop)
        if(prop!=FKEventName::g_commonProperty && customVisors(prop).toList().contains(client)){
//            FK_MLOGV("roperty update submit",prop)
            event=FKEvent::makeEvent(getID(),prop,property(prop.toUtf8()),recievers);
            event->isPropertyNotifier=true;
            emit newEvent(event);
        }
    }
}

/*!
 * \brief Refresh value of property prop for totalWatchers
 */

void FKObject::refreshCommonValue(const QString& prop){
    if(propList().contains(prop)){
        FKEvent* event;
        QList<qint32> recievers;
        foreach (FKObject* client, totalVisors()) {
            recievers.append(client->getID());
        }
        if(prop!=FKEventName::g_commonProperty){
            event=FKEvent::makeEvent(getID(),prop,property(prop.toUtf8()),recievers);
            event->isPropertyNotifier=true;
            emit newEvent(event);
        }else{
            FK_MLOG("unable refresh common value: invalid common property")
        }
    }else{
        FK_MLOGV("unable refresh common value, no such property",prop)
    }
}

/*!
 * \brief Using by loaded room to make additional initialization (visual objects etc.)
 */

void FKObject::initObject(){
    if(!_initialized){
        _initialized=initialization();
    }
}

bool FKObject::initialization(){
    if(_room && _room->isActive())return true;
    return false;
}

void FKObject::deinitObject(){
    if(_initialized){
        deinitialization();
        _initialized=false;
    }
}

void FKObject::setActive(const bool &t){
    if(servant)servant->active=t;
}

/*!
 * \fn bool FKObject::initialization()
 * \brief Function contains additional initialisation for loaded rooms (visual objects etc.)
 */

/*!
 * \brief Convinient function returns id of object's room or -1 if no room
 */
qint32 FKObject::getRoomID() const{
    if(_room){
        return _room->getID();
    }else{
        return -1;
    }
}

/*!
 * \fn FKRoom* FKObject::room()const
 * \brief Returns object's room
 */

/*!
\brief Reveal common properties of \c watched to object

This required for event/action delivery
 */

bool FKObject::setCommonWatching(FKObject *watched){
    return setCustomWatching(watched,FKEventName::g_commonProperty);
}

/*!
\brief Reveals custom property of \c watched to object.

Always auto-reveals common properties.
If no property specified, reveals all avaliable custom properties.
 */

bool FKObject::setCustomWatching(FKObject *watched,const QString &prop){
    if(servant){
        QStringList props;
        if(prop==QString()){
            props=watched->propList();
        }else{
            props.append(prop);
        }
        if(!props.contains(FKEventName::g_commonProperty))props.append(FKEventName::g_commonProperty);
        int count=0;
        foreach (QString p, props){
            if((!servant->watcheds.value(p).contains(watched))
                    && (!watched->servant->watchers.value(p).contains(this))){
                servant->watcheds[p].insert(watched);
                watched->servant->watchers[p].insert(this);
                ++count;
            }
        }
        if(count){
            watched->resetSharedVisorsCalculation();
            foreach (FKObject* cl, totalVisors())cl->visionChanged();
        }
        return count;
    }else{
        FK_MLOG("error set watching: no servant")
    }
    return false;
}

/*!
 * \fn QStringList FKObject::eventList()const
 * \brief Returns list of all supported FK-events
 */

/*!
 * \fn QStringList FKObject::actionList()const
 * \brief Returns list of all supported FK-actions
 */

/*!
 * \brief Returns list of all supported FK-properties
 */
QStringList FKObject::propList()const{
    return QStringList(FKEventName::g_commonProperty);
}

void FKObject::initLevels(){
    if(_room && _room->isActive())initLevel();
}

/*!
 * \fn void FKObject::executeEvent(FKEvent* ev)
 * \brief Pass event to subject
 */

/*!
 * \fn void FKObject::executeAction(FKEvent* ac)
 * \brief Pass action to subject
 */

/*!
\brief Reveals shared watching of \c watched.

Always auto-reveals common properties.
Shared watching reveal all \c watched 's revealed properties, but not watched's own properties
 */

bool FKObject::setSharedWatching(FKObject *watched){
    if(servant){
        if((!servant->sharedWatcheds.contains(watched))
                && (!watched->servant->sharedWatchers.contains(this))){
            servant->sharedWatcheds.insert(watched);
            watched->servant->sharedWatchers.insert(this);
            setCommonWatching(watched);
            watched->resetSharedVisorsCalculation();
            foreach (FKObject* cl, totalVisors())cl->visionChanged();
            return true;
        }
    }else{
        FK_MLOG("error set shared watching: no servant")
    }
    return false;
}

/*!
 * \brief Reveals common and all custom properties and shared watching as well
 */

bool FKObject::setTotalWatching(FKObject *watched){
    bool a=setCustomWatching(watched);
    bool b=setSharedWatching(watched);
    return a || b;
}

/*!
\brief Hide custom property of \c watched.

If property not specified, hide all custom properties
 */

bool FKObject::stopCustomWatching(FKObject *watched,const QString &prop){
    if(servant){
        QStringList props;
        if(prop==QString()){
            props=watched->propList();
        }else{
            props.append(prop);
        }
        int count=0;
        foreach (QString p, props){
            if((servant->watcheds.value(p).contains(watched))
                    && (watched->servant->watchers.value(p).contains(this))){
                servant->watcheds[p].remove(watched);
                watched->servant->watchers[p].remove(this);
                ++count;
            }
        }
        if(count){
            watched->resetSharedVisorsCalculation();
            foreach (FKObject* cl, totalVisors())cl->visionChanged();
        }
        return count;
    }else{
        FK_MLOG("error stop watching: no servant")
    }
    return false;
}

/*!
 * \brief Hide shared watching of \c watched
 */

bool FKObject::stopSharedWatching(FKObject *watched){
    if(servant){
        if((servant->sharedWatcheds.contains(watched))
                && (watched->servant->sharedWatchers.contains(this))){
            servant->sharedWatcheds.remove(watched);
            watched->servant->sharedWatchers.remove(this);
            watched->resetSharedVisorsCalculation();
            foreach (FKObject* cl, totalVisors())cl->visionChanged();
            return true;
        }
    }else{
        FK_MLOG("error stop shared watching: no servant")
    }
    return false;
}

/*!
 * \brief Hide all FK-properties and shared vision of object
 */

void FKObject::stopTotalWatching(FKObject *watched){
    if(!servant){
        FK_MLOG("Unable stop watching: no servant")
        return;
    }
    QList<FKObject*> lst;
    if(watched){
        lst.append(watched);
    }else{
        lst.append(servant->watcheds.value(FKEventName::g_commonProperty).toList());
    }
    foreach (FKObject* obj, lst) {
        stopCustomWatching(obj);
        stopSharedWatching(obj);
    }
}

/*!
 * \brief Returns object id
 */
qint32 FKObject::getID()const{
    return _objID;
}

/*!
 * \brief Send event for target to target's totalVisors according EventTarget. User is optional argument that accelerate event delivery.
 *
 *  Use this for normal events
 */
void FKObject::doEvent(FKObject* target,const QString &eventName,const QVariant &value, FKObject::EventTarget eventTarget, qint32 user){
    QList<qint32> recievers;
    foreach(FKObject* obj,target->totalVisors())recievers.append(obj->getID());
    if(eventTarget==FKObject::ToAllClients)recievers.append(-2);
    FKEvent* e=FKEvent::makeEvent(target->getID(), eventName, value, recievers, user);
    if(eventTarget==FKObject::ToClientsAndServer)target->executeEvent(e);
    emit newEvent(e);
}

/*!
 * \brief This is overloaded function for empty value
 */
void FKObject::doEvent(FKObject *target,const QString &eventName, FKObject::EventTarget eventTarget, qint32 user){
    doEvent(target,eventName,QVariant(),eventTarget,user);
}

/*!
 * \brief Send event for target to eventTarget's totalVisors. Used ToAllClients behaivour.
 * Use this for player-specific events, f.e. displaying win message
 */
void FKObject::doEvent(FKObject *target,const QString &eventName,const QVariant &value, FKObject *eventTarget, qint32 user){
    QList<qint32> recievers;
    foreach(FKObject* obj,eventTarget->totalVisors())recievers.append(obj->getID());
    recievers.append(-2);
    FKEvent* e=FKEvent::makeEvent(target->getID(), eventName, value, recievers, user);
    emit newEvent(e);
}

/*!
 * \brief This is overloaded function for empty value
 */

void FKObject::doEvent(FKObject *target,const QString &eventName, FKObject *eventTarget, qint32 user){
    doEvent(target,eventName,QVariant(),eventTarget,user);
}

/*!
 * \brief Send error event for all target's totalVisors.
 * Use this for display error messages
 */

void FKObject::doEvent(const QString &errorText, FKObject *target){
    FK_MLOGV("Error generated by FKObject",errorText)
    foreach(FKObject* obj,target->totalVisors()){
        FKEvent* e=FKEvent::errorEvent(errorText,obj->getID());
        emit newEvent(e);
    }
}
